package com.codefish;

import java.util.concurrent.atomic.AtomicInteger;

/**
 * 计数器类，用于统计某个站点的被访问次数<br>
 * 要保证一个类在全局只有一个实例，那么这个类需要满足以下特点:<br>
 * <ol>
 *     <li>类不能被继承</li>
 *     <li>类不能被克隆</li>
 *     <li>类不能被序列化</li>
 *     <li>类只能被实例化一次且在内部被实例化，
 *     对外暴露一个能够获取这个单例对象的接口</li>
 * </ol><br>
 * *单例创建方式分为懒汉式和饿汉式，对于懒汉式单例的创建，在多线程环境下需要采用双检锁策略。该示例代码采用懒汉式双检锁策略创建单例
 *
 * @author codefish
 * @version 1.0
 * @date 2022/05/28 下午 08:20
 */
public final class GlobalCounter {
    /**
     * 全局唯一的实例对象
     */
    private static volatile GlobalCounter instance;
    /**
     * 使用原子int类来实现计数
     */
    private final AtomicInteger count = new AtomicInteger(0);

    /**
     * 构造函数声明为private，防止外部实例化对象
     */
    private GlobalCounter() {

    }


    /**
     * 计数器加一并返回新的值<br>
     * 计数采用CAS自旋策略
     *
     * @return 完成自增后的值
     */
    public int increment() {
        //记录原先的值
        int expectedValue;
        //记录新的值
        int newValue;
        //若设置值成功，则会跳出循环
        //否则一直自旋
        do {
            //获取count的当前值
            expectedValue = count.get();
            //新的值为当前值+1
            newValue = expectedValue + 1;
        } while (!count.compareAndSet(expectedValue, newValue));
        //返回新值
        return newValue;
    }

    /**
     * 获取当前计数值
     *
     * @return 当前计数值
     */
    public int getCount() {
        return count.get();
    }


    /**
     * 获取单例，常规双检锁写法
     *
     * @return 唯一的instance
     */
    public static GlobalCounter getInstance() {
        if (instance == null) {
            synchronized (GlobalCounter.class) {
                if (instance == null) {
                    instance = new GlobalCounter();
                }

            }
        }
        return instance;

    }


}
